export interface VectorData {
  vector: number[];
  timestamp: number;
  close_ma_rate?: number;
  similarity?: number;
}

export class SimilarityMetrics {
  // 计算余弦相似度
  static cosineSimilarity(vector1: number[], vector2: number[]): number {
    // 检查向量长度是否相同
    if (vector1.length !== vector2.length) {
      throw new Error("Vectors must have the same length.");
    }

    // 计算点积
    let dotProduct = 0;
    for (let i = 0; i < vector1.length; i++) {
      dotProduct += vector1[i] * vector2[i];
    }

    // 计算向量范数
    const norm1 = Math.sqrt(
      vector1.reduce((acc, val) => acc + val * val, 0)
    );
    const norm2 = Math.sqrt(
      vector2.reduce((acc, val) => acc + val * val, 0)
    );

    // 计算余弦相似度
    const similarity = dotProduct / (norm1 * norm2);
    return similarity;
  }
  // 计算欧氏距离
  static euclideanDistance(vector1: number[], vector2: number[]): number {
    // 检查向量长度是否相同
    if (vector1.length !== vector2.length) {
      throw new Error("Vectors must have the same length.");
    }

    let sumOfSquaredDifferences = 0;
    for (let i = 0; i < vector1.length; i++) {
      const difference = vector1[i] - vector2[i];
      sumOfSquaredDifferences += difference * difference;
    }

    const euclideanDistance = Math.sqrt(sumOfSquaredDifferences);
    return euclideanDistance;
  }

  // 计算曼哈顿距离
  static manhattanDistance(vector1: number[], vector2: number[]): number {
    // 检查向量长度是否相同
    if (vector1.length !== vector2.length) {
      throw new Error(`Vectors must have the same length. ${vector1.length} - ${vector2.length}`);
    }

    let sumOfAbsoluteDifferences = 0;
    for (let i = 0; i < vector1.length; i++) {
      const difference = Math.abs(vector1[i] - vector2[i]);
      sumOfAbsoluteDifferences += difference;
    }

    return sumOfAbsoluteDifferences;
  }

  static cosineSimilarityTop<T extends VectorData[]>(kline_vectors: T, vector: number[],  handler?: (data: VectorData) => boolean) {
    const result = [] as T & {similarity: number};
    for (let i = 0; i < kline_vectors.length; i++) {
      const element = kline_vectors[i];
      const similarity = SimilarityMetrics.cosineSimilarity(element.vector, vector);

      if (similarity !== 0 && (handler === undefined || handler(element))) {
        result.push({ similarity, ...element })
      }
    }

    return result.sort(function (a, b) {
      return b.similarity - a.similarity
    }).slice(0, 5)
  }
  static manhattanSimilarityTop<T extends VectorData>(kline_vectors: T[], vector: number[],  handler?: (data: VectorData) => boolean) {
    const result = [] as Array<T & {similarity: number}>;
    for (let i = 0; i < kline_vectors.length; i++) {
      const element = kline_vectors[i];
      const similarity = SimilarityMetrics.manhattanDistance(element.vector, vector);

      // if (Math.sign(element.close_ma_rate) === Math.sign(close_ma_rate)) {
      //   result.push({ similarity, ...element })
      // }
      if (handler === undefined || handler(element)) {
        result.push({ similarity, ...element })
      }
    }

    return result.sort(function (a, b) {
      return a.similarity - b.similarity
    }).slice(0, 5)
  }

  static euclideanSimilarityTop<T extends VectorData[]>(kline_vectors: T, vector: number[],  handler?: (data: VectorData) => boolean ) {
    const result = [] as T & {similarity: number};
    for (let i = 0; i < kline_vectors.length; i++) {
      const element = kline_vectors[i];
      const similarity = SimilarityMetrics.manhattanDistance(element.vector, vector);

      if (handler === undefined || handler(element)) {
        result.push({ similarity, ...element })
      }
    }

    return result.sort(function (a, b) {
      return a.similarity - b.similarity
    }).slice(0, 5)
  }
}
